// phalanx pair.hpp.cu header file

// Copyright (c) 2011 - 2011 Kohei Takahashi (Flast).
// Distributed under the MIT license. for more detail see COPYING.

#ifndef IG_PHALANX_UTILITY_PAIR_HPP_CU_ONCE_
#define IG_PHALANX_UTILITY_PAIR_HPP_CU_ONCE_

#include <phalanx/config.hpp.cu>

#include <cstddef>

#include <boost/mpl/placeholders.hpp>

#include <phalanx/type_traits/is_const.hpp.cu>
#include <phalanx/type_traits/remove_const.hpp.cu>
#include <phalanx/type_traits/add_const.hpp.cu>

#include <phalanx/utility/apply_if.hpp.cu>

namespace phalanx
{

// TODO: Support move semantics by Phalanx.Move.
template < typename T1, typename T2 >
struct pair
{
    typedef T1 first_type;
    typedef T2 second_type;

    first_type  first;
    second_type second;

    PHALANX_CONFIG_DEFAULT_FUNCTION( true )
    pair( void ) {}

    PHALANX_CONFIG_DEFAULT_FUNCTION( true )
    pair( const pair &_p )
      : first( _p.first ), second( _p.second ) {}

    PHALANX_CONFIG_DEFAULT_FUNCTION( true )
    pair( const first_type &_x, const second_type &_y )
      : first( _x ), second( _y ) {}

    template < typename U, typename V >
    PHALANX_CONFIG_DEFAULT_FUNCTION( true )
    pair( const pair< U, V > &_p )
      : first( _p.first ), second( _p.second ) {}
};

template < typename T1, typename T2 >
PHALANX_CONFIG_DEFAULT_FUNCTION( true )
bool
operator==( const pair< T1, T2 > &_x, const pair< T1, T2 > &_y )
{ return _x.first == _y.first && _x.second == _y.second; }

template < typename T1, typename T2 >
PHALANX_CONFIG_DEFAULT_FUNCTION( true )
bool
operator<( const pair< T1, T2 > &_x, const pair< T1, T2 > &_y )
{
    return _x.first < _y.first
        || ( !( _y.first < _x.first ) && _x.second < _y.second );
}

template < typename T1, typename T2 >
PHALANX_CONFIG_DEFAULT_FUNCTION( true )
bool
operator!=( const pair< T1, T2 > &_x, const pair< T1, T2 > &_y )
{ return !( _x == _y ); }

template < typename T1, typename T2 >
PHALANX_CONFIG_DEFAULT_FUNCTION( true )
bool
operator>( const pair< T1, T2 > &_x, const pair< T1, T2 > &_y )
{ return _y < _x; }

template < typename T1, typename T2 >
PHALANX_CONFIG_DEFAULT_FUNCTION( true )
bool
operator<=( const pair< T1, T2 > &_x, const pair< T1, T2 > &_y )
{ return !( _y < _x ); }

template < typename T1, typename T2 >
PHALANX_CONFIG_DEFAULT_FUNCTION( true )
bool
operator>=( const pair< T1, T2 > &_x, const pair< T1, T2 > &_y )
{ return !( _x < _y ); }


template < typename T1, typename T2 >
PHALANX_CONFIG_DEFAULT_FUNCTION( true )
pair< T1, T2 >
make_pair( T1 _x, T2 _y )
{ return pair< T1, T2 >( _x, _y ); }


template < ::std::size_t, typename >
struct tuple_element;

template < ::std::size_t N, typename T1, typename T2 >
struct tuple_element< N, pair< T1, T2 > > {};

template < typename T1, typename T2 >
struct tuple_element< 0, pair< T1, T2 > >
{ typedef T1 type; };

template < typename T1, typename T2 >
struct tuple_element< 1, pair< T1, T2 > >
{ typedef T2 type; };


template < typename >
struct tuple_size;

template < typename T1, typename T2 >
struct tuple_size< pair< T1, T2 > >
{ static const ::std::size_t value = 2; };


namespace pair_detail
{

template < ::std::size_t >
struct _get_lookup_helper;

template <>
struct _get_lookup_helper< 0 >
{
    template < typename T >
    PHALANX_CONFIG_DEFAULT_FUNCTION( true )
    static typename lazy_apply_if<
      is_const< T >,
      add_const< ::boost::mpl::_1 >,
      tuple_element< 0, typename remove_const< T >::type > >::type &
    _get( T &_p )
    { return _p.first; }
};

template <>
struct _get_lookup_helper< 1 >
{
    template < typename T >
    PHALANX_CONFIG_DEFAULT_FUNCTION( true )
    static typename lazy_apply_if<
      is_const< T >,
      add_const< ::boost::mpl::_1 >,
      tuple_element< 1, typename remove_const< T >::type > >::type &
    _get( T &_p )
    { return _p.second; }
};

} // namespace pair_detail

template < ::std::size_t N, typename T1, typename T2 >
PHALANX_CONFIG_DEFAULT_FUNCTION( true )
typename tuple_element< N, pair< T1, T2 > >::type &
get( pair< T1, T2 > &_p )
{ return pair_detail::_get_lookup_helper< N >::_get( _p ); }

template < ::std::size_t N, typename T1, typename T2 >
PHALANX_CONFIG_DEFAULT_FUNCTION( true )
const typename tuple_element< N, pair< T1, T2 > >::type &
get( const pair< T1, T2 > &_p )
{ return pair_detail::_get_lookup_helper< N >::_get( _p ); }

} // namespace phalanx

#endif // IG_PHALANX_UTILITY_PAIR_HPP_CU_ONCE_

